﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeDeeds.Debug
{
   public static class PerformanceTest
   {
      public static TimeSpan Meassure(Action action, bool prallel = false)
      {
         return Meassure(action, 1, null, null, prallel);
      }

      public static TimeSpan Meassure(Action action, string title, bool prallel = false)
      {
         return Meassure(action, 1, title, null, prallel);
      }

      public static TimeSpan Meassure(Action action, int repetitions, bool prallel = false)
      {
         return Meassure(action, repetitions, null, null, prallel);
      }

      public static TimeSpan Meassure(Action action, string title, TextWriter output, bool prallel = false)
      {
         return Meassure(action, 1, title, output, prallel);
      }

      public static TimeSpan Meassure(Action action, int repetitions, string title, bool prallel = false)
      {
         return Meassure(action, repetitions, title, null, prallel);
      }

      public static TimeSpan Meassure(Action action, int repetitions, string title, TextWriter output, bool prallel = false)
      {
         if (1 > repetitions)
         {
            repetitions = 1;
         }

         if (string.IsNullOrEmpty(title))
         {
            title = "Test action";
         }

         int gcCount = System.GC.CollectionCount(0) + System.GC.CollectionCount(1) + System.GC.CollectionCount(2);
         TimeSpan firstRun = TimeSpan.MinValue;
         Stopwatch stopwatch = Stopwatch.StartNew();
         if (prallel)
         {
            Parallel.For(0, repetitions, i =>
            {
               action();
               if (i == 0)
               {
                  firstRun = stopwatch.Elapsed;
               }
            });
         }
         else
         {
            for (int i = 0; i < repetitions; ++i)
            {
               action();
               if (i == 0)
               {
                  firstRun = stopwatch.Elapsed;
               }
            }
         }
         TimeSpan total = stopwatch.Elapsed;
         TimeSpan average = TimeSpan.FromTicks(total.Ticks / repetitions);
         
         gcCount = (System.GC.CollectionCount(0) + System.GC.CollectionCount(1) + System.GC.CollectionCount(2)) - gcCount;
         
         if (null != output)
         {
            if (1 < repetitions)
            {
               output.WriteLine("{0} - First run tok {1}.", title, firstRun);
               output.WriteLine("{0} - Average time per run tok {1}.", title, average);
            }
            output.WriteLine("{0} - Total time for {1} runs tok {2}.", title, repetitions, total);
            output.WriteLine("{0} - Total number of GC collections {1}.", title, gcCount);
         }

         if (1 < repetitions)
         {
            System.Diagnostics.Debug.WriteLine("{0} - First run tok {1}.", title, firstRun);
            System.Diagnostics.Debug.WriteLine("{0} - Average time per run tok {1}.", title, average);
         }
         System.Diagnostics.Debug.WriteLine("{0} - Total time for {1} runs tok {2}.", title, repetitions, total);
         System.Diagnostics.Debug.WriteLine("{0} - Total number of GC collections {1}.", title, gcCount);

         return total;
      }
   }
}
